package ru.classbase.dbdumper.writer

import com.fasterxml.jackson.core.JsonFactory
import com.fasterxml.jackson.core.JsonGenerator
import org.springframework.stereotype.Component
import ru.classbase.dbdumper.model.Column
import ru.classbase.dbdumper.model.DbMetaData
import ru.classbase.dbdumper.model.DumpContext
import ru.classbase.dbdumper.model.OutputFormat
import java.sql.ResultSet
import java.time.format.DateTimeFormatter


@Component
class JsonWriter(
    private val dateFormatter: DateTimeFormatter,
    private val dateTimeFormatter: DateTimeFormatter,
) : DumpWriter {

    override val supportedFormat =  OutputFormat.JSON

    override fun write(metaData: DbMetaData, context: DumpContext) {
        val (_, _, columns, rs, writer) = context

        val jf = JsonFactory()

        jf.createGenerator(writer).use { gen ->

            gen.writeStartArray()

            while (rs.next()) {
                writeRow(rs, gen, columns)
            }

            gen.writeRaw('\n')
            gen.writeEndArray()
        }
    }

    private fun writeRow(rs: ResultSet, gen: JsonGenerator, columns: List<Column>) {
        gen.writeRaw('\n')
        gen.writeStartObject()

        for (i in 1..columns.size) {

            gen.writeFieldName(columns[i - 1].columnName)

            when (columns[i - 1].dataType) {
                "integer" -> writeInt(gen, rs, i)
                "date" -> writeDate(gen, rs, i)
                "smallint" -> writeSmallInt(gen, rs, i)
                "timestamp without time zone" -> writeTimestamp(gen, rs, i)
                "boolean" -> writeBoolean(gen, rs, i)
                "character varying" -> writeString(gen, rs, i)
                "numeric" -> writeNumeric(gen, rs, i)
                "bigint" -> writeBigInt(gen, rs, i)
                else -> throw IllegalArgumentException("Тип данных [${columns[i - 1].dataType}]  не поддерживается")
            }

        }
        gen.writeEndObject()

    }

    fun writeString(gen: JsonGenerator, rs: ResultSet, colNum: Int) {
        val value = rs.getString(colNum)

        if (rs.wasNull()) {
            gen.writeNull()
            return
        }

        gen.writeString(value)
    }

    fun writeBoolean(gen: JsonGenerator, rs: ResultSet, colNum: Int) {
        val value = rs.getBoolean(colNum)

        if (rs.wasNull()) {
            gen.writeNull()
            return
        }

        gen.writeBoolean(value)
    }

    fun writeSmallInt(gen: JsonGenerator, rs: ResultSet, colNum: Int) {
        val value = rs.getShort(colNum)

        if (rs.wasNull()) {
            gen.writeNull()
            return
        }

        gen.writeNumber(value)
    }

    fun writeInt(gen: JsonGenerator, rs: ResultSet, colNum: Int) {
        val value = rs.getInt(colNum)

        if (rs.wasNull()) {
            gen.writeNull()
            return
        }

        gen.writeNumber(value)
    }

    fun writeBigInt(gen: JsonGenerator, rs: ResultSet, colNum: Int) {
        val value = rs.getLong(colNum)

        if (rs.wasNull()) {
            gen.writeNull()
            return
        }

        gen.writeNumber(value)
    }

    fun writeNumeric(gen: JsonGenerator, rs: ResultSet, colNum: Int) {
        val value = rs.getBigDecimal(colNum)

        if (rs.wasNull()) {
            gen.writeNull()
            return
        }
        gen.writeNumber(value)
    }

    fun writeDate(gen: JsonGenerator, rs: ResultSet, colNum: Int) {
        val value = rs.getDate(colNum)

        if (rs.wasNull()) {
            gen.writeNull()
            return
        }

        gen.writeString(dateFormatter.format(value.toLocalDate()))
    }

    fun writeTimestamp(gen: JsonGenerator, rs: ResultSet, colNum: Int) {
        val value = rs.getTimestamp(colNum)

        if (rs.wasNull()) {
            gen.writeNull()
            return
        }

        gen.writeString(dateTimeFormatter.format(value.toLocalDateTime()))
    }
}